343. 整数拆分

343. 整数拆分

题目链接
代码随想录

分析

我一开始看到这道题目的时候没有任何思路,但是我首先将 dp[i] 数组定义为,i 拆分成多个正整数之后,其乘积最大值,然后我推演了一下 i 从 2 到 10 的所有的 dp[i] 的值。
这里讲解一个重要多 dp 的技巧:dp 的大部分提都是用数学归纳法,而数学归纳法大部分都是永远求解数列问题的,所以只要你的 dp 定义的没错,你把 dp 的前 10 位推演出来,推演着推演着就有思路了。

然后我看了代码随想录的方案,了解到了一个简单的思路。
dp 数组的定义还是 i 拆分成多个正整数之后,其乘积最大值。但是求 dp[i] 的时候,用 for 循环所有拆成两个数的情况找出来,拆分出的两个数为 j 和 i-j,然后比较 j*(i-j)j*dp[i-j] 哪个更大,为什么不是拆分成 dp[j]*dp[i-j],其实因为 j 是从 1 到 i-1,所以 j 实际上是已经把所有的情况都考虑在内了,比如 i 等于 10,当前 j 为 6,j 可以拆分成 2 和 4 ,那么当前应该计算 2*4*dp[4] ,但是因为 j 是从 1 开始遍历的,所以实际上我们已经遍历过了 j=2 和 j=4 的情况,j=2 时候,另一半为 dp[8],j=4 时候,另一半为 dp[6],这些乘积的大小我们前面也都比较过,也就是说,j 拆分与 j 不拆分的情况我们都已经对比过了。所以不会有问题。这一点确实有点不好理解。
我想出来的思路只用了一个 for 循环,这个标准方法用了 for 循环嵌套,所以效率会不高。
一开始我拒绝使用两层 for 循环的思路,其实用两层 for 循环也可以,不是用两层就代表不行。

解题

public int integerBreak(int n) {
    int[] dp = new int[n + 1];
    dp[2] = 1;
    if (n == 2) {
        return dp[n];
    }
    dp[3] = 2;
    if (n == 3) {
        return dp[n];
    }
    dp[4] = 4;
    if (n == 4) {
        return dp[n];
    }
    dp[5] = 6;
    if (n == 5) {
        return dp[n];
    }
    dp[6] = 9;
    if (n == 6) {
        return dp[n];
    }
    dp[7] = 12;
    if (n == 7) {
        return dp[n];
    }
    for (int i = 8; i <= n; i++) {
        dp[i] = Math.max(Math.max(2 * dp[i - 2], 3 * dp[i - 3]), 4 * dp[i - 4]);
    }
    return dp[n];
}

容易想到的官方版本

public int integerBreak(int n) {
    int[] dp = new int[n + 1];
    dp[2] = 1;
    for (int i = 3; i <= n; i++) {
        for (int j = 1; j <= i - 1; j++) {
            dp[i] = Math.max(dp[i], Math.max(j * dp[i - j], j * (i - j)));
        }
    }
    return dp[n];
}

相关题